using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.Events;

public class AspectRatioController : MonoBehaviour
{
	[Serializable]
	public class ResolutionChangedEvent : UnityEvent<int, int, bool>
	{
	}

	private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

	private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

	public struct RECT
	{
		public int Left;

		public int Top;

		public int Right;

		public int Bottom;
	}

	public ResolutionChangedEvent resolutionChangedEvent;

	[SerializeField]
	private bool allowFullscreen = true;

	[SerializeField]
	private float aspectRatioWidth = 16f;

	[SerializeField]
	private float aspectRatioHeight = 9f;

	[SerializeField]
	private int minWidthPixel = 512;

	[SerializeField]
	private int minHeightPixel = 512;

	[SerializeField]
	private int maxWidthPixel = 2048;

	[SerializeField]
	private int maxHeightPixel = 2048;

	private float aspect;

	private int setWidth = -1;

	private int setHeight = -1;

	private bool wasFullscreenLastFrame;

	private bool started;

	private int pixelHeightOfCurrentScreen;

	private int pixelWidthOfCurrentScreen;

	private bool quitStarted;

	private const int WM_SIZING = 532;

	private const int WMSZ_LEFT = 1;

	private const int WMSZ_RIGHT = 2;

	private const int WMSZ_TOP = 3;

	private const int WMSZ_BOTTOM = 6;

	private const int GWLP_WNDPROC = -4;

	private WndProcDelegate wndProcDelegate;

	private const string UNITY_WND_CLASSNAME = "UnityWndClass";

	private IntPtr unityHWnd;

	private IntPtr oldWndProcPtr;

	private IntPtr newWndProcPtr;

	[DllImport("kernel32.dll")]
	private static extern uint GetCurrentThreadId();

	[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
	private static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

	[DllImport("user32.dll")]
	private static extern bool EnumThreadWindows(uint dwThreadId, EnumWindowsProc lpEnumFunc, IntPtr lParam);

	[DllImport("user32.dll")]
	private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

	[DllImport("user32.dll", SetLastError = true)]
	private static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);

	[DllImport("user32.dll")]
	private static extern bool GetClientRect(IntPtr hWnd, ref RECT lpRect);

	[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetWindowLong")]
	private static extern IntPtr SetWindowLong32(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

	[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetWindowLongPtr")]
	private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

	private void Start()
	{
		if (Application.isEditor)
		{
			return;
		}
		Application.wantsToQuit += ApplicationWantsToQuit;
		EnumThreadWindows(GetCurrentThreadId(), delegate(IntPtr hWnd, IntPtr lParam)
		{
			StringBuilder stringBuilder = new StringBuilder("UnityWndClass".Length + 1);
			GetClassName(hWnd, stringBuilder, stringBuilder.Capacity);
			if (stringBuilder.ToString() == "UnityWndClass")
			{
				unityHWnd = hWnd;
				return false;
			}
			return true;
		}, IntPtr.Zero);
		SetAspectRatio(aspectRatioWidth, aspectRatioHeight, apply: true);
		wasFullscreenLastFrame = Screen.fullScreen;
		wndProcDelegate = wndProc;
		newWndProcPtr = Marshal.GetFunctionPointerForDelegate(wndProcDelegate);
		oldWndProcPtr = SetWindowLong(unityHWnd, -4, newWndProcPtr);
		started = true;
	}

	public void SetAspectRatio(float newAspectWidth, float newAspectHeight, bool apply)
	{
		aspectRatioWidth = newAspectWidth;
		aspectRatioHeight = newAspectHeight;
		aspect = aspectRatioWidth / aspectRatioHeight;
		if (apply)
		{
			Screen.SetResolution(Screen.width, Mathf.RoundToInt((float)Screen.width / aspect), Screen.fullScreen);
		}
	}

	private IntPtr wndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
	{
		if (msg == 532)
		{
			RECT structure = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));
			RECT lpRect = default(RECT);
			GetWindowRect(unityHWnd, ref lpRect);
			RECT lpRect2 = default(RECT);
			GetClientRect(unityHWnd, ref lpRect2);
			int num = lpRect.Right - lpRect.Left - (lpRect2.Right - lpRect2.Left);
			int num2 = lpRect.Bottom - lpRect.Top - (lpRect2.Bottom - lpRect2.Top);
			structure.Right -= num;
			structure.Bottom -= num2;
			int num3 = Mathf.Clamp(structure.Right - structure.Left, minWidthPixel, maxWidthPixel);
			int num4 = Mathf.Clamp(structure.Bottom - structure.Top, minHeightPixel, maxHeightPixel);
			switch (wParam.ToInt32())
			{
			case 1:
				structure.Left = structure.Right - num3;
				structure.Bottom = structure.Top + Mathf.RoundToInt((float)num3 / aspect);
				break;
			case 2:
				structure.Right = structure.Left + num3;
				structure.Bottom = structure.Top + Mathf.RoundToInt((float)num3 / aspect);
				break;
			case 3:
				structure.Top = structure.Bottom - num4;
				structure.Right = structure.Left + Mathf.RoundToInt((float)num4 * aspect);
				break;
			case 6:
				structure.Bottom = structure.Top + num4;
				structure.Right = structure.Left + Mathf.RoundToInt((float)num4 * aspect);
				break;
			case 8:
				structure.Right = structure.Left + num3;
				structure.Bottom = structure.Top + Mathf.RoundToInt((float)num3 / aspect);
				break;
			case 5:
				structure.Right = structure.Left + num3;
				structure.Top = structure.Bottom - Mathf.RoundToInt((float)num3 / aspect);
				break;
			case 7:
				structure.Left = structure.Right - num3;
				structure.Bottom = structure.Top + Mathf.RoundToInt((float)num3 / aspect);
				break;
			case 4:
				structure.Left = structure.Right - num3;
				structure.Top = structure.Bottom - Mathf.RoundToInt((float)num3 / aspect);
				break;
			}
			setWidth = structure.Right - structure.Left;
			setHeight = structure.Bottom - structure.Top;
			structure.Right += num;
			structure.Bottom += num2;
			resolutionChangedEvent.Invoke(setWidth, setHeight, Screen.fullScreen);
			Marshal.StructureToPtr(structure, lParam, fDeleteOld: true);
		}
		return CallWindowProc(oldWndProcPtr, hWnd, msg, wParam, lParam);
	}

	private void Update()
	{
		if (!allowFullscreen && Screen.fullScreen)
		{
			Screen.fullScreen = false;
		}
		if (Screen.fullScreen && !wasFullscreenLastFrame)
		{
			int num;
			int num2;
			if (aspect < (float)pixelWidthOfCurrentScreen / (float)pixelHeightOfCurrentScreen)
			{
				num = pixelHeightOfCurrentScreen;
				num2 = Mathf.RoundToInt((float)pixelHeightOfCurrentScreen * aspect);
			}
			else
			{
				num2 = pixelWidthOfCurrentScreen;
				num = Mathf.RoundToInt((float)pixelWidthOfCurrentScreen / aspect);
			}
			Screen.SetResolution(num2, num, fullscreen: true);
			resolutionChangedEvent.Invoke(num2, num, arg2: true);
		}
		else if (!Screen.fullScreen && wasFullscreenLastFrame)
		{
			Screen.SetResolution(setWidth, setHeight, fullscreen: false);
			resolutionChangedEvent.Invoke(setWidth, setHeight, arg2: false);
		}
		else if ((Screen.fullScreen || setWidth == -1 || setHeight == -1 || (Screen.width == setWidth && Screen.height == setHeight)) && !Screen.fullScreen)
		{
			pixelHeightOfCurrentScreen = Screen.currentResolution.height;
			pixelWidthOfCurrentScreen = Screen.currentResolution.width;
		}
		wasFullscreenLastFrame = Screen.fullScreen;
	}

	private static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
	{
		if (IntPtr.Size == 4)
		{
			return SetWindowLong32(hWnd, nIndex, dwNewLong);
		}
		return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
	}

	private bool ApplicationWantsToQuit()
	{
		if (!started)
		{
			return false;
		}
		if (!quitStarted)
		{
			StartCoroutine("DelayedQuit");
			return false;
		}
		return true;
	}

	private IEnumerator DelayedQuit()
	{
		SetWindowLong(unityHWnd, -4, oldWndProcPtr);
		yield return new WaitForEndOfFrame();
		quitStarted = true;
		Application.Quit();
	}
}
